﻿#include "stdafx.h"
#if _QG_USE_ES2
#include "qg_es2_dev.h"
#include "qg_es2_mtrl.h"
#include "qg_es2_cnv.h"
#include "qg_trace.h"
#include "qg_math.h"

// 스크린삿
qgImage* ES2Device::ScreenCapture()
{
	return NULL;
}

//
bool ES2Device::_ActiveTexture(kint stage) throw()
{
	if (_caps.supp_multi_texture)
	{
		if (_vprn.m_tex_active != stage)
		{
			_vprn.m_tex_active = stage;
			glActiveTexture(GL_TEXTURE0 + stage);
		}

		return true;
	}
	else
	{
		if (stage > 0)
			return false;
		else
		{
			_vprn.m_tex_active = 0;
			return true;
		}
	}
}

//
bool ES2Device::_CommitTexture(kint stage, qgTexture* tex) throw()
{
	if (!_ActiveTexture(stage))
		return false;

	if (tex)
	{
		ES2Texture* etex = (ES2Texture*)tex;
		GLuint id = (GLuint)etex->GetDescription();

		glBindTexture(GL_TEXTURE_2D, id);

		return true;
	}
	else
	{
		// 아래꺼 안해도 될것같은데...
		//glBindTexture(GL_TEXTURE_2D, 0);

		return false;
	}
}

//
qgTexture* ES2Device::_RoamImplTexture(const qgArprTexture& ap)
{
	ES2Texture* tex;

	if (K_OFLAG(ap.flags, QGTEXF_OFF))
	{
		ES2TextureFBO* off = kobj_create(ES2TextureFBO);
		tex = off;

		if (!off->_InitProperty(ap.type, ap.name, ap.width, ap.height, ap.depth, ap.mips, ap.flags, ap.fmt, this) ||
			!off->_CreateTexture(NULL, this) ||
			!off->_CreateOff(this))
		{
			tex->Unload();
			tex = NULL;
		}
	}
	else
	{
		tex = kobj_create(ES2Texture);

		if (!tex->_InitProperty(ap.type, ap.name, ap.width, ap.height, ap.depth, ap.mips, ap.flags, ap.fmt, this) ||
			!tex->_CreateTexture(NULL, this))
		{
			tex->Unload();
			tex = NULL;
		}
	}

	// 만드는 동안 스테이지 0을 썼기에 리셋
	kobj_unload(_vprn.m_tex[0]);
	_vprn.m_tex[0] = NULL;
	_vprn.bd_tex[0] = true;

	return tex;
}

//
qgTexture* ES2Device::_RoamImplTexture(const qgArprImage& ap)
{
	ES2Texture* tex = kobj_create(ES2Texture);

	if (!tex->_InitImage(ap.name, ap.img, 0, ap.flags, this) ||
		!tex->_CreateTexture(ap.img, this))
	{
		tex->Unload();
		tex = NULL;
	}

	// 만드는 동안 스테이지 0을 썼기에 리셋
	kobj_unload(_vprn.m_tex[0]);
	_vprn.m_tex[0] = NULL;
	_vprn.bd_tex[0] = true;

	return tex;
}

//
qgTexture* ES2Device::_RoamImplMotionTexture(const char* filename, kint flags)
{
	ES2TextureMotion* tex = kobj_create(ES2TextureMotion);

	if (!tex->_CreateMotion(filename, flags, this))
	{
		tex->Unload();
		tex = NULL;
	}

	// 만드는 동안 스테이지 0을 썼기에 리셋
	kobj_unload(_vprn.m_tex[0]);
	_vprn.m_tex[0] = NULL;
	_vprn.bd_tex[0] = true;

	return tex;
}

//
qgDepthBuffer* ES2Device::_RoamImplDepthBuffer(const tpl::Point& size)
{
	ES2DepthBuffer* buf = kobj_create(ES2DepthBuffer);

	if (!buf->_Create(size.width, size.height, this))
	{
		buf->Unload();
		buf = NULL;
	}

	// 만드는 동안 스테이지 0을 썼을 수도 있기에 리셋, 버퍼 타입이면 안씀. 오직 텍스쳐 타입 만
	kobj_unload(_vprn.m_tex[0]);
	_vprn.m_tex[0] = NULL;
	_vprn.bd_tex[0] = true;

	return buf;
}


//////////////////////////////////////////////////////////////////////////
// 텍스쳐
KIMPL_SUBCLASS(ES2Texture, "ES2Texture");

//
ES2Texture::ES2Texture() throw()
: _es_fmt(GL_RGBA), _pixel_fmt(GL_BGRA_EXT), _pixel_type(GL_UNSIGNED_BYTE)
{
}

//
ES2Texture::~ES2Texture() throw()
{
	if (_node_desc)
	{
		GLuint n = (GLuint)_node_desc;
		glDeleteTextures(1, &n);
	}

	kobj_unload(_img_rsw);
	kobj_unload(_img_mip);
}

//
bool ES2Texture::_InitImage(const char* name, qgImage* img, kint mips, kint flags, ES2Device* dev)
{
	kint width = img->GetWidth();
	kint height = img->GetHeight();
	kint depth = 1;
	qgColorFormat fmt = img->GetColorFormat();

	return _InitProperty(QGTEXTURE_2D, name, width, height, depth, mips, flags, fmt, dev);
}

//
bool ES2Texture::_InitProperty(qgTextureType type, const char* name, kint width, kint height, kint depth, kint mips, kint flags, qgColorFormat fmt, ES2Device* dev)
{
	//
	_key = name ? k_strdup(name) : k_apsprintf("es2_texture_%Lu", k_nth());
	_textype = type;
	_flags = flags;
	_mips = mips;
	_fmt = fmt == QGCF_NONE ? QGCF32_BGRA : fmt;

	// 포맷 맞춤
	_es_type = ES2Convert::TextureType(_textype);
	_es_fmt = ES2Convert::DetectColorFormat(_fmt, _pixel_fmt, _pixel_type, _alpha);

	if (_es_type == 0 || _es_fmt == GL_NONE)
	{
		// 지원하지 못하는 포맷
		return false;
	}

	// 플래그 검사
	if (K_OFLAG(flags, QGRESF_DYNAMIC))
	{
		if (K_OFLAG(flags, QGTEXF_OFF))
		{
			// 다이나믹 + FBO 금지
			return false;
		}

		if (!K_OFLAG(flags, QGTEXF_NOMIP))
		{
			// 다이나믹 -> 밉맵 금지
			K_YFLAG(_flags, QGTEXF_NOMIP, true);
		}

		_mips = 1;

		// OPENGL은 바로 매핑이 없으므로 이미지를 강제로 처리하게 함
		K_YFLAG(_flags, QGTEXF_IMAGE, true);
	}
	else
	{
		if (K_OFLAG(flags, QGTEXF_OFF))
		{
			if (!K_OFLAG(flags, QGTEXF_NOMIP))
			{
				// 오프 -> 밉맵 금지
				K_YFLAG(_flags, QGTEXF_NOMIP, true);
			}

			K_YFLAG(_insts, QGTEXI_OFF, true);
			_mips = 1;
		}
		else
		{
			if (K_OFLAG(flags, QGTEXF_NOMIP))
				_mips = 1;
			else
			{
				K_YFLAG(_insts, QGTEXI_AUTOMIP, true);
				_mips = 0;
			}
		}
	}

	// 최대 크기로 맞춤
	kint maxsize = dev->GetCaps().max_texture_dimension;

	if (width < maxsize)
		_width = width;
	else
	{
		_width = maxsize;
		K_YFLAG(_insts, QGTEXI_CLAMP, true);
	}

	if (height < maxsize)
		_height = height;
	else
	{
		_height = maxsize;
		K_YFLAG(_insts, QGTEXI_CLAMP, true);
	}

	// ES2는 3D 텍스쳐가 없지만 ES3에는 있다... 일단 내비두자
	if (depth < maxsize)
		_depth = depth;
	else
	{
		_depth = maxsize;
		K_YFLAG(_insts, QGTEXI_CLAMP, true);
	}

	// 정방 맞춤
	if (dev->GetCaps().supp_texture_npo2)
	{
		_awidth = _width;
		_aheight = _height;
		_adepth = _depth;
	}
	else
	{
		_awidth = _math::OptimizeTextureSize(_width, true, 0);
		_aheight = _math::OptimizeTextureSize(_height, true, 0);
		_adepth = _math::OptimizeTextureSize(_depth, true, 0);

		if (_awidth != _width || _aheight != _height /*|| _adepth != _depth*/)
			K_YFLAG(_insts, QGTEXI_ADJPOW2, true);
	}

	// 샘플러 초기화
	_splr = qgSplrDataEx(QGFLT_NONE, QGADR_NONE, QGADR_NONE, QGADR_NONE, QGCOP_NONE, tpl::ColorValues::Black, 0.0f, tpl::Vec2(-FLT_MAX, FLT_MAX));

	// 모션
	if (K_OFLAG(flags, QGTEXF_MOTION))
		K_YFLAG(_insts, QGTEXI_MOTION, true);

	// 밉맵 개수를 얻는 함수가 없네... 계산으로 만들어야함
	if (_mips == 0)
	{
		// ceil(log_2(max(width, height))) + 1
		//kint maxv = K_MAX(_awidth, _aheight);
		// 아 귀찮...
	}

	return true;
}

//
bool ES2Texture::_CreateInternal(GLuint* ptex, ES2Device* dev)
{
	// 텍스쳐 핸들 만들고, 일단 자기 자신에다 넣는다.
	// 어짜피 진짜 핸들은 나중에 설정할 테니 믿고 넣자
	glGenTextures(1, ptex);
	_node_desc = (kuintptr)*ptex;

	dev->_CommitTexture(0, this);

	// 추가 설정부터 해주고
	if (K_OFLAG(_insts, QGTEXI_OFF))
	{
		// FBO용 추가 설정
		glTexParameteri(_es_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(_es_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	}

	if (K_OFLAG(_insts, QGTEXI_AUTOMIP))
	{
		// 자동 밉맵
		glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
		glTexParameteri(_es_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(_es_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	}
	else
	{
		glTexParameteri(_es_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(_es_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	}

	// 텍스쳐 만들기
	switch (K_CAST_ENUM(_textype))
	{
		case QGTEXTURE_1D:	// 1D도 2D로 처리함
		case QGTEXTURE_2D:
			glTexImage2D(GL_TEXTURE_2D, 0, _es_fmt, _awidth, _aheight, 0, _pixel_fmt, _pixel_type, NULL);
			break;

		case QGTEXTURE_CUBE:
			for (kint f = 0; f < 6; f++)
				glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, 0, _es_fmt, _awidth, _aheight, 0, _pixel_fmt, _pixel_type, NULL);
			break;
	}

	//dev->_GetError(NULL, true);

	return true;
}

//
bool ES2Texture::_CreateTexture(qgImage* img, ES2Device* dev)
{
	GLuint tex;

	if (!img)
	{
		// 빈 텍스쳐 만들기
		if (!_CreateInternal(&tex, dev))
			return false;
	}
	else
	{
		// 이미지
#if 1
		if (K_OFLAG(_insts, QGTEXI_ADJPOW2 | QGTEXI_CLAMP) == 0)
		{
			// ES2는 BGRA로 읽기 때문에 걍 씀
			_img_rsw = img;
			img->Load();
		}
		else
		{
			_img_rsw = kobj_new(qgImage, _awidth, _aheight, _fmt);
			img->CopyToScale(_img_rsw);
		}
#else
		_img_rsw = kobj_new(qgImage, _awidth, _aheight, _fmt);
		K_OFLAG(_insts, QGTEXI_ADJPOW2 | QGTEXI_CLAMP)
			? img->CopyToScale(_img_rsw)
			: img->CopyTo(_img_rsw);
#endif

		// 텍스쳐 핸들
		if (!_CreateInternal(&tex, dev))
			return false;

		// 업로드
		_UploadTexture(true, 0, dev);

		// 보관할 필요가 없으면 이미지 지움
		if (!K_OFLAG(_flags, QGTEXF_IMAGE))
		{
			_img_rsw->Unload();
			_img_rsw = NULL;
		}
	}

	// 내부 함수에서 이미 설정했기 때문에 할필요없음
	//_node_desc = (kuintptr)tex;

	return true;
}

//
void ES2Texture::_UploadTexture(bool isnew, kint mipno, ES2Device* dev)
{
	qgImage* img = mipno == 0 ? _img_rsw : _img_mip;
	k_return_if_fail(img);

	// 텍스쳐 이미지 처리
	if (!isnew)
	{
		dev->_CommitTexture(0, this);

		glTexSubImage2D(GL_TEXTURE_2D, mipno, 0, 0, img->GetWidth(), img->GetHeight(), _pixel_fmt, _pixel_type, img->GetData());
	}
	else
	{
		// 처음 만들 때 설정을 넣는데, 여기 없어도 되는 루틴이긴 하지 않음?
		if (mipno == 0)
		{
			if (K_OFLAG(_insts, QGTEXI_AUTOMIP))
			{
				// 자동 밉맵
				glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST);
				glTexParameteri(_es_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
				glTexParameteri(_es_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
			}
			else
			{
				glTexParameteri(_es_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
				glTexParameteri(_es_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			}
		}

		glTexImage2D(GL_TEXTURE_2D, mipno, _es_fmt, img->GetWidth(), img->GetHeight(), 0, _pixel_fmt, _pixel_type, img->GetData());
	}

	// 밉맵 자동
	if (mipno == 0 && K_OFLAG(_insts, QGTEXI_AUTOMIP))
		glGenerateMipmap(_es_type);
}

//
bool ES2Texture::EnterMap(qgTextureMap* retdata, kint flag, kint mipno, qgTextureFace face)
{
	k_return_value_if_fail(retdata, false);
	k_return_value_if_fail(_lock_mip < 0, false);

	// 잠글 수 있는 포맷인가
	GLenum pxfmt, pxtype;
	if (!ES2Convert::TestLockableFormat(_fmt, pxfmt, pxtype))
		return false;

	//
	qgImage* img = _img_rsw;

	// 이미지가 없으면 만들기 (오프텍스쳐도 포함)
	if (!img || K_OFLAG(_insts, QGTEXI_OFF))
	{
		if (!img)
		{
			if (mipno == 0)
				img = _img_rsw = kobj_new(qgImage, _awidth, _aheight, _fmt);
			else
			{
				int i = 0, w = _awidth, h = _aheight;

				do
				{
					if (w > 1)
						w >>= 1;

					if (h > 1)
						h >>= 1;

					i++;
				} while (i != mipno);

				img = _img_mip = kobj_new(qgImage, w, h, _fmt);
			}
		}

		// 디바이스에서 이미지 다운로드
		if (K_OFLAG(flag, QGENTF_READ) && _es_type == GL_TEXTURE_2D)
		{
			// glGetTexImage()가 없다.
			// FBO로 만들어서 glReadPixels()로 읽어야함
			// ...인데 귀찮아서 패스
		}
	}

	// 리턴값
	retdata->data = img->GetData();
	retdata->pitch = img->GetPitch();
	retdata->slice = 0;

	retdata->width = img->GetWidth();
	retdata->height = img->GetHeight();
	retdata->depth = 0;

	// 잠금 저장
	_lock_mip = mipno;
	_lock_face = face;

	return true;
}

//
bool ES2Texture::LeaveMap()
{
	k_return_value_if_fail(_lock_mip >= 0, false);

	// 그냥? 아님 밉맵?
	qgImage* img = _img_mip ? _img_mip : _img_rsw;
	k_return_value_if_fail(img, false);

	// 올림
	_UploadTexture(false, _lock_mip, (ES2Device*)_dev);

	// 지우라면 지우지요
	if (_img_mip)
	{
		_img_mip->Unload();
		_img_mip = NULL;
	}
	else if (!K_OFLAG(_flags, QGTEXF_IMAGE))
	{
		_img_rsw->Unload();
		_img_rsw = NULL;
	}

	// 정리
	_lock_mip = -1;
	_lock_face = QGTXFX_NONE;

	return true;
}


//////////////////////////////////////////////////////////////////////////
// 모션 텍스쳐
KIMPL_SUBCLASS(ES2TextureMotion, "ES2TextureMotion");

//
ES2TextureMotion::ES2TextureMotion() throw()
{
}

//
ES2TextureMotion::~ES2TextureMotion() throw()
{
	// 렌더러 마다 텍스쳐 해제가 다르므로 여기서 함
	if (_mt_preimgs.IsEmpty())
		CleanStreams();
	else
	{
#if _SB_64_
		kForEach(kuintptr n, _mt_preimgs)
		{
			GLuint tex = (GLuint)n;
			glDeleteTextures(1, &tex);
		}
#else
		// Uint가 아니고 uintptr이라 이거 못씀. 32비트에선 되겠네.. 해보까?
		static_assert(sizeof(kuintptr) == sizeof(GLuint), "Texture storage size problem. try upper code.");
		glDeleteTextures(_mt_count, _mt_preimgs.Data());
#endif
	}
}

//
qgITextureMotion* ES2TextureMotion::GetMotion()
{
	return this;
}

//
bool ES2TextureMotion::MotionUpdate(float adv)
{
	k_return_value_if_fail(_lock_mip < 0, false);

	//
	kuintptr ptr;
	bool res;

	if (!ManualUpdate(adv, res, ptr))
		return false;

	if (res)
	{
		ES2Device* dev = (ES2Device*)_dev;
		dev->_CommitTexture(0, this);

		// 실제 텍스쳐 사이즈랑 다르면 다르게 처리해야함
		// 반드시 _es_type==GL_TEXTURE_2D 임.
		qgImage* img;

		if (_img_rsw)
		{
			// 이미지를 늘려서 그린다
			// 우와! 이거 엄청 느리다!!!!
			img = _img_rsw;
			qgBlitImage::StrechBlit(QG_BLITOP_TEX, img, NULL, _mt_ucmimage, NULL, tpl::ColorValues::Transparent);
			glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img->GetWidth(), img->GetHeight(), _pixel_fmt, _pixel_type, img->GetData());
		}
		else
		{
			// 무조건 32비트 RGBA만 사용함
			img = _mt_ucmimage;
			glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img->GetWidth(), img->GetHeight(), _pixel_fmt, _pixel_type, img->GetData());
		}
	}
	else if (ptr != 0)
	{
		if (ptr != _node_desc)
		{
			// 헐... 참조가 엄꾸나
			_node_desc = ptr;
		}
	}

	return true;
}

//
bool ES2TextureMotion::_CreateMotion(const char* filename, kint flags, ES2Device* dev)
{
	// 스트림
	kStream* stm = dev->FileOpen(filename, true);
	if (stm == NULL)
		return NULL;

	if (!qgFwSupp::CheckHeadAndTail(stm))
	{
		stm->Unload();
		return NULL;
	}

	// 모션
	if (!ParseMotion(stm, K_OFLAG(flags, QGTEXF_PRELOAD)))
		return false;

	// D11과의 호환성을 위해서는 RGBA -> BGRA를 쓰는 것이 좋다... 지만 먼저 D11부터 고쳐야함
	qgImage* img = _mt_ucmimage;

	if (_mt_preimgs.IsEmpty())
	{
		// 한장 갖다 잠궈 씀
		if (!_InitProperty(QGTEXTURE_2D, filename, img->GetWidth(), img->GetHeight(), 1, 1, QGTEXF_S2D | QGRESF_DYNAMIC | QGTEXF_MOTION, QGCF32_RGBA, dev) ||
			!_CreateTexture(NULL, dev))
			return false;

		// 실제 텍스쳐 사이즈랑 다르면 버퍼를 다시 만들어야함 -> 잠그기용
		if (K_OFLAG(_insts, QGTEXI_ADJPOW2 | QGTEXI_ADJSQR))
			_img_rsw = kobj_new(qgImage, _awidth, _aheight, _fmt);
	}
	else
	{
		// 모두 만들어 놓기
		if (!_InitProperty(QGTEXTURE_2D, filename, img->GetWidth(), img->GetHeight(), 1, 1, QGTEXF_S2D | QGTEXF_MOTION, QGCF32_RGBA, dev))
			return false;

		// 실제 텍스쳐 사이즈랑 다르면 버퍼를 다시 만들어야함 -> 잠그기용
		qgImage* imgexp;

		if (K_OFLAG(_insts, QGTEXI_ADJPOW2 | QGTEXI_ADJSQR) == 0)
			imgexp = NULL;
		else
		{
			_img_rsw = kobj_new(qgImage, _awidth, _aheight, _fmt);
			imgexp = _img_rsw;
		}

		//
		GLuint tex;

		for (kuint i = 0; i < _mt_count; i++)
		{
			if (!_CreateInternal(&tex, dev))
			{
				// 오류 난 프레임만 패스
				tex = 0;
			}
			else
			{
				// 이미지를 만들어 써 넣는다
				if (UpdateStreamImage(i))
				{
					if (imgexp)
					{
						// 이미지 늘려서 그림
						qgBlitImage::StrechBlit(QG_BLITOP_TEX, imgexp, NULL, img, NULL, tpl::ColorValues::Transparent);
						glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, imgexp->GetWidth(), imgexp->GetHeight(), _pixel_fmt, _pixel_type, imgexp->GetData());
					}
					else
					{
						// 무조건 32비트 RGBA만 사용함
						glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img->GetWidth(), img->GetHeight(), _pixel_fmt, _pixel_type, img->GetData());
					}
				}
			}

			_mt_preimgs[i] = (kuintptr)tex;
		}

		// 0번 넣어 둠
		_node_desc = _mt_preimgs[0];

		// 정리
		CleanStreams();

		K_YFLAG(_insts, QGTEXI_LIST, true);
	}

	//
	return true;
}


//////////////////////////////////////////////////////////////////////////
// 오프 텍스쳐
KIMPL_SUBCLASS(ES2TextureFBO, "ES2TextureFBO");

//
ES2TextureFBO::ES2TextureFBO() throw()
{
}

//
ES2TextureFBO::~ES2TextureFBO() throw()
{
	if (_fbo != 0)
		glDeleteFramebuffers(1, &_fbo);

	if (_db)
		_db->Unload();
}

//
qgITextureOff* ES2TextureFBO::GetOff()
{
	return this;
}

//
bool ES2TextureFBO::_CreateOff(ES2Device* dev)
{
	GLuint fbo = 0;

	// 프레임버퍼
	glGenFramebuffers(1, &fbo);
	if (fbo == 0)
		return false;

	glBindFramebuffer(GL_FRAMEBUFFER, fbo);
	//glDrawBuffer(GL_COLOR_ATTACHMENT0);

	// 프레임버퍼에 텍스쳐 붙임
	dev->_CommitTexture(0, this);	// 아마도 여러번 바인드 될듯...

	switch (K_CAST_ENUM(_textype))
	{
		case QGTEXTURE_2D:
			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, (GLuint)_node_desc, 0);
			break;

		case QGTEXTURE_3D:		// 미구현
		case QGTEXTURE_CUBE:	// 미구현
		default:
			glBindFramebuffer(GL_FRAMEBUFFER, 0);
			glDeleteFramebuffers(1, &fbo);
			return false;
	}

	// 검사
	const char* chk = _CheckFBO();

	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	if (chk != NULL)
	{
		kobj_tracet(KLV_ERROR, KERR_INVAL, chk);

		glDeleteFramebuffers(1, &fbo);

		return false;
	}

	// 오케
	_fbo = fbo;

	return true;
}

//
const char* ES2TextureFBO::_CheckFBO()
{
	GLenum s = glCheckFramebufferStatus(GL_FRAMEBUFFER);
	const char* msg;

	switch (s)
	{
		case GL_FRAMEBUFFER_COMPLETE:
			msg = NULL;
			break;

		case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
			msg = "incomplete image attachments";
			break;

		case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
			msg = "missing image";
			break;

		case GL_FRAMEBUFFER_UNSUPPORTED:
			msg = "unsupported format";
			break;

		default:
			msg = "unknown error";
			break;
	}

	return msg;
}

bool ES2TextureFBO::EnterOff(kint clearance, const tpl::ColorValue& color)
{
	if (K_OFLAG(_insts, QGTEXI_LOCKOFF))
		return false;

	ES2Device* dev = (ES2Device*)_dev;

	// 깊이 버퍼
	if (_db == NULL)
	{
		_db = (ES2DepthBuffer*)dev->CreateDepthBuffer(_awidth, _aheight);

		if (_db != NULL && !_db->Attach(this))
		{
			_db->Unload();
			_db = NULL;
		}
	}
	else
	{
		if (_db->GetLastAttach() != this && !_db->Attach(this))
		{
			_db->Unload();
			_db = NULL;
		}
	}

	// 설정
	dev->SetTexture(0, NULL);
	dev->_CommitTexture(0, NULL);
	dev->_SetRenderTarget(this);	// 뷰포트 바꿈

	// 바인드
	glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
	//glDrawBuffer(GL_COLOR_ATTACHMENT0);

	dev->_InternalClear(clearance, tpl::Color(color), 0, 1.0f);

	// 올ㅋ
	K_YFLAG(_insts, QGTEXI_LOCKOFF, true);

	return true;
}

bool ES2TextureFBO::LeaveOff()
{
	if (!K_OFLAG(_insts, QGTEXI_LOCKOFF))
		return false;

	ES2Device* dev = (ES2Device*)_dev;

	//
	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	//
	dev->_SetRenderTarget(NULL);
	dev->SetTexture(0, NULL);

	//
	K_YFLAG(_insts, QGTEXI_LOCKOFF, false);

	return true;
}


//////////////////////////////////////////////////////////////////////////
// 뎁스 버퍼
KIMPL_SUBCLASS(ES2DepthBuffer, "ES2DepthBuffer");

#define USE_RENDERBUFFER_DEPTH	1

//
ES2DepthBuffer::ES2DepthBuffer() throw()
{
}

//
ES2DepthBuffer::~ES2DepthBuffer() throw()
{
	if (_node_desc != 0)
	{
		GLuint n = (GLuint)_node_desc;
#if USE_RENDERBUFFER_DEPTH
		glDeleteRenderbuffers(1, &n);
#else
		glDeleteTextures(1, &n);
#endif
	}
}

//
bool ES2DepthBuffer::_Create(kuint width, kuint height, ES2Device* dev)
{
	bool usestencil = dev->GetCaps().supp_stencil;
	GLuint dsb;

#if USE_RENDERBUFFER_DEPTH
	// 렌더 버퍼 사용
	glGenRenderbuffers(1, &dsb);
	if (dsb == 0)
		return false;

	glBindRenderbuffer(GL_RENDERBUFFER, dsb);

	// 프레임버퍼에 깊이 렌더 버퍼 붙임
	if (usestencil)
	{
		// 스텐실 해봄
		glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL_OES, width, height);

		if (glGetError() == GL_NO_ERROR)
			_stencil = true;
		else
		{
			// 스텐실이 문제냐.... 그렇다면 포기
			glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
			_stencil = false;
		}
	}
	else
	{
		// 스텐실 포기
		glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
		_stencil = false;
	}

	glBindRenderbuffer(GL_RENDERBUFFER, 0);
#else
	// 텍스쳐 사용
	dev->_CommitTexture(0, NULL);

	glGenTextures(1, &dsb);
	glBindTexture(GL_TEXTURE_2D, dsb);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	// 프레임버퍼에 깊이 렌더 버퍼 붙임
	if (usestencil)
	{
		// 스텐실 해봄
		glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_STENCIL_OES, width, height, 0,
			GL_DEPTH_STENCIL_OES, GL_UNSIGNED_INT_24_8_OES, NULL);

		if (glGetError() == GL_NO_ERROR)
			_stencil = true;
		else
		{
			// 음... 스텐실 포기
			glTexImage2D(GL_TEXTURE_2D, 0, ES2Convert::DepthComponent(dev->GetCaps().val_depth_bpp),
				width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
			_stencil = false;
		}
	}
	else
	{
		// 스텐실 포기
		glTexImage2D(GL_TEXTURE_2D, 0, ES2Convert::DepthComponent(dev->GetCaps().val_depth_bpp),
			width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
		_stencil = false;
	}

	dev->_CommitTexture(0, NULL);
#endif

	//
	_node_desc = dsb;
	_width = width;
	_height = height;

	return true;
}

//
bool ES2DepthBuffer::Attach(ES2TextureFBO* texfbo)
{
	k_return_value_if_fail(texfbo, false);

	GLuint fbo = (GLuint)texfbo->GetDescription();
	GLuint dsb = (GLuint)_node_desc;

	glBindFramebuffer(GL_FRAMEBUFFER, fbo);
	//glDrawBuffer(GL_COLOR_ATTACHMENT0);

#if USE_RENDERBUFFER_DEPTH
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, dsb);

	if (_stencil)
		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, dsb);
#else
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, dsb, 0);

	if (_stencil)
		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, dsb, 0);
#endif

	//
	const char* chk = ES2TextureFBO::_CheckFBO();

	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	if (chk != NULL)
	{
		kobj_tracet(KLV_ERROR, KERR_INVAL, chk);

		return false;
	}

	//
	_lastfbo = texfbo;	// 참조 안함...

	return true;
}

#endif	// _QG_USE_ES2

